/* @flow */

import type Watcher from './watcher'
import config from '../config'
import {callHook, activateChildComponent} from '../instance/lifecycle'

import {
  warn,
  nextTick,
  devtools
} from '../util/index'

export const MAX_UPDATE_COUNT = 100

//存放一次schedule的所有watcher，schedule执行完成之后
//会清空该数组
const queue: Array<Watcher> = []
const activatedChildren: Array<Component> = []
//该对象的形式为{watcherId1:true, watcherId2:true}，其中键为watcher.id值，
//该对象用于判断某watcher是否已经存在，防止重复添加同一个watcher
let has: { [key: number]: ?true } = {}
let circular: { [key: number]: number } = {}
//用于标记是否运行了queueWatcher方法，防止重复添加flushSchedulerQueue方法到
//tick中
let waiting = false
//标记flushSchedulerQueue方法是否运行
let flushing = false
let index = 0

/**
 * Reset the scheduler's state.
 */
function resetSchedulerState() {
  index = queue.length = activatedChildren.length = 0
  has = {}
  if (process.env.NODE_ENV !== 'production') {
    circular = {}
  }
  waiting = flushing = false
}

/**
 * Flush both queues and run the watchers.
 */
function flushSchedulerQueue() {
  flushing = true
  let watcher, id

  // Sort queue before flush.
  // This ensures that:
  // 1. Components are updated from parent to child. (because parent is always
  //    created before the child)
  // 2. A component's user watchers are run before its render watcher (because
  //    user watchers are created before the render watcher)
  // 3. If a component is destroyed during a parent component's watcher run,
  //    its watchers can be skipped.
  /**
   * 给queue排序，按照id的值由小到大的排序，这样做可以保证：
   * 1.组件更新的顺序是从父组件到子组件的顺序，因为父组件总是比子组件先创建。
   * 2.一个组件的user watchers（也就是通过vue的watch选项设置的，或者通过$watch添加的）比render watcher先运行，
   * 因为user watchers往往比render watcher更早创建
   * 3.如果一个组件在父组件watcher运行期间被销毁，它的watcher执行将被跳过
   */
  queue.sort((a, b) => a.id - b.id)

  // do not cache length because more watchers might be pushed
  // as we run existing watchers
  for (index = 0; index < queue.length; index++) {
    watcher = queue[index]
    id = watcher.id
    has[id] = null
    watcher.run()
    // in dev build, check and stop circular updates.
    if (process.env.NODE_ENV !== 'production' && has[id] != null) {
      circular[id] = (circular[id] || 0) + 1
      if (circular[id] > MAX_UPDATE_COUNT) {
        warn(
          'You may have an infinite update loop ' + (
            watcher.user
              ? `in watcher with expression "${watcher.expression}"`
              : `in a component render function.`
          ),
          watcher.vm
        )
        break
      }
    }
  }

  // keep copies of post queues before resetting state
  const activatedQueue = activatedChildren.slice()
  const updatedQueue = queue.slice()

  resetSchedulerState()

  // call component updated and activated hooks
  //详情可查看create-components.js --> insert hook中的关于queueActivatedComponent方法的注释
  callActivatedHooks(activatedQueue)
  callUpdatedHooks(updatedQueue)

  // devtool hook
  /* istanbul ignore if */
  if (devtools && config.devtools) {
    devtools.emit('flush')
  }
}

//为什么不在_update方法中调用updated钩子方法，而在此处调用？
//答：这样可以保证vue实例及其子实例都已更新完成。
function callUpdatedHooks(queue) {
  let i = queue.length
  while (i--) {
    const watcher = queue[i]
    const vm = watcher.vm
    //重渲染，_isMounted标记vm已挂载成功
    if (vm._watcher === watcher && vm._isMounted) {
      callHook(vm, 'updated')
    }
  }
}

/**
 * Queue a kept-alive component that was activated during patch.
 * The queue will be processed after the entire tree has been patched.
 * 可查看queueActivatedComponent方法调用处的英文以及中文注释。
 */
export function queueActivatedComponent(vm: Component) {
  // setting _inactive to false here so that a render function can
  // rely on checking whether it's in an inactive tree (e.g. router-view)
  vm._inactive = false
  activatedChildren.push(vm)
}

function callActivatedHooks(queue) {
  for (let i = 0; i < queue.length; i++) {
    queue[i]._inactive = true
    activateChildComponent(queue[i], true /* true */)
  }
}

/**
 * Push a watcher into the watcher queue.
 * Jobs with duplicate IDs will be skipped unless it's
 * pushed when the queue is being flushed.
 */
export function queueWatcher(watcher: Watcher) {
  const id = watcher.id
  //防止重复添加同一个watcher
  if (has[id] == null) {
    has[id] = true
    //flushing用于标记当前是否正在调用所有的watcher进行更新
    if (!flushing) {
      queue.push(watcher)
    } else {
      // if already flushing, splice the watcher based on its id
      // if already past its id, it will be run next immediately.
      let i = queue.length - 1
      //index是当前运行的watcher的下标位置
      //为什么要执行queue[i].id > watcher.id判断？
      //因为id越小说明越早创建，具体可查看flushSchedulerQueue方法中的
      //queue.sort((a, b) => a.id - b.id)的解释
      while (i > index && queue[i].id > watcher.id) {
        i--
      }
      //将watcher插入到正在执行的队列中
      queue.splice(i + 1, 0, watcher)
    }
    // queue the flush
    //waiting用于保证不会同时执行多次flushSchedulerQueue方法
    if (!waiting) {
      waiting = true
      nextTick(flushSchedulerQueue)
    }
  }
}
